
#include <malloc.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#include "SDK/amx/amx.h"
#include "SDK/plugincommon.h"


#include "hook.h"
#include "amx.h"

struct cb_node_t
{
	int amx_idx;
	AMX *amx_file;
} nick, chat;

/*
static int nick_idx = -1;
static AMX *nick_amx = NULL;


static int chat_idx = -1;
static AMX *chat_amx = NULL;
*/

//static struct cb_node_t nick, chat;


extern void * pAMXFunctions;

typedef void (*logprintf_t)(const char* format, ...);

static logprintf_t logprintf;


static cell AMX_NATIVE_CALL n_installhook( AMX* amx, cell* params )
{
	unsigned int type = params[1],
		addr = params[2],
		func_addr = params[3];

	switch(type)
	{
	case 1: // e8
		return hook_e8(addr, func_addr);
	case 2: // ff15
		return hook_ff15(addr, func_addr);
	case 3: // 8b1d
		return hook_8b1d(addr, func_addr);
	case 4: // e9
		return hook_e9(addr, func_addr);
	}


	return 0;

}


static cell AMX_NATIVE_CALL n_getfuncaddr( AMX* amx, cell* params )
{
	int type = params[1];

	switch(type)
	{
	case 1: //
		return samp_yes;
	case 2: //
		return samp_no;
	case 3: // nick
		return samp_is_bad_nick;
	case 4: // chat
		return samp_on_chat_print;
	case 5: // void
		return samp_void;
	}


	return 0;

}

static cell AMX_NATIVE_CALL n_registercb( AMX* amx, cell* params )
{
	int type = params[1], tmp;

	char *func;
	amx_StrParam(amx, params[2], func);


	switch(type)
	{
	case 3: //
		if(amx_FindPublic(amx, func, &tmp) == AMX_ERR_NONE)
		{
			nick.amx_file = amx;
			nick.amx_idx = tmp;
			return 1;
		}
		//else
		return 0;

	case 4: //
		if(amx_FindPublic(amx, func, &tmp) == AMX_ERR_NONE)
		{
			chat.amx_file = amx;
			chat.amx_idx = tmp;
			return 1;
		}
		//else
		return 0;


	//default:
		//return 0;
	}


	return 0;

}

static cell AMX_NATIVE_CALL n_i( AMX* amx, cell* params )
{
  int addr = params[1], obj = params[2];
  call_i(addr, obj);
  return 1;
}
static cell AMX_NATIVE_CALL n_iii( AMX* amx, cell* params )
{
  int addr = params[1], obj = params[2];
  int i1 = params[3], i2 = params[4];
  call_iii(addr, obj, i1, i2);
  return 1;
}
static cell AMX_NATIVE_CALL n_iissi( AMX* amx, cell* params )
{
  int addr = params[1], obj = params[2];
  int i1 = params[3], i2 = params[6];
  char * s1, * s2;
  amx_StrParam(amx, params[4], s1);
  amx_StrParam(amx, params[4], s2);
  call_iissi(addr, obj, i1, s1, s2, i2);
  return 1;
}


int onnickcheck_call(const char *nick_)
{
	//logprintf("calling function %d in %p for %s", nick_idx, nick_amx, nick);
	if(nick.amx_idx > -1 && nick.amx_file != NULL)
	{
		int amx_ret;
		cell amx_addr,
			*amx_phys_addr;
		amx_PushString(nick.amx_file, &amx_addr, &amx_phys_addr, nick_, 0, 0);
		amx_Exec(nick.amx_file, (cell *) &amx_ret, nick.amx_idx);
		amx_Release(nick.amx_file, amx_addr);
		return amx_ret;
	}
	return 0;
}


int onchat_call(const char *format, const char *nick, const char *msg)
{
	//logprintf("calling function %d in %p for %s", nick_idx, nick_amx, nick);
	if(chat.amx_idx > -1 && chat.amx_file != NULL)
	{
		int amx_ret;
		cell amx_addr[3],
			*amx_phys_addr;
		// push
		amx_PushString(chat.amx_file, &amx_addr[0], &amx_phys_addr, msg, 0, 0);
		amx_PushString(chat.amx_file, &amx_addr[1], &amx_phys_addr, nick, 0, 0);
		amx_PushString(chat.amx_file, &amx_addr[2], &amx_phys_addr, format, 0, 0);
		// call
		amx_Exec(chat.amx_file, (cell *) &amx_ret, chat.amx_idx);
		// pop
		for(int i=0; i<3; i++)
			amx_Release(chat.amx_file, amx_addr[i]);
		return amx_ret;
	}
	return 0;
}


AMX_NATIVE_INFO myhookAMXNatives[ ] =
{
	{"myhook_install",  n_installhook },
	{"myhook_getfunc",  n_getfuncaddr },
	{"myhook_register", n_registercb },
	{"myhook_call_i", n_i },
	{"myhook_call_iii", n_iii },
	{"myhook_call_iissi", n_iissi },
	{0,                0}
};

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad( AMX *amx )
{
	//printf("nick_idx: %d, amx: %p", nick_idx, amx);
	/*
	int tmp;
	if(nick_idx < 0 && amx_FindPublic(amx, "OnNickCheck", &tmp) == AMX_ERR_NONE)
	{
		nick_amx = amx;
		nick_idx = tmp;
	}
	*/
	return amx_Register( amx, myhookAMXNatives, -1 );
}

PLUGIN_EXPORT int PLUGIN_CALL AmxUnload( AMX *amx )
{
    return AMX_ERR_NONE;
}

PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports()
{
    return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}

PLUGIN_EXPORT bool PLUGIN_CALL Load( void **ppData )
{
    pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
	logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];

	init_hooks();

    logprintf("  Plugin myhooks got loaded. Version: " __DATE__ ",  " __TIME__);
    return true;
}

PLUGIN_EXPORT void PLUGIN_CALL Unload( )
{
    logprintf("  Plugin myhooks got unloaded.");
}
